package com.code.soulnetty.modbus.biz.msg;

import com.code.soulnetty.modbus.biz.Bits;
import com.code.soulnetty.modbus.client.service.DataType;
import lombok.Getter;
import lombok.ToString;

/**
 * @author jdy
 * @date 10:11 上午
 **/
@Getter
@ToString
public abstract class Message implements Comparable<Message> {
    private int channelId;
    /**
     * 单元标示符/slaveid
     */
    private final short unitId;
    /**
     * 功能码
     */
    private final byte fc;
    /**
     * 偏移量
     */
    private final int offset;
    /**
     * 对应设备ID
     */
    private final String deviceGuid;
    /**
     * 对应点位code
     */
    private final String pointCode;
    /**
     * 数据类型
     */
    private DataType dataType;

    /**
     * AI/DI/AO/DO
     */
    private String pointDataType;

    /**
     * 是否小端在前
     */
    private boolean littleEndian;
    /**
     * 交换寄存器顺序
     */
    private boolean registerSwap;
    /**
     * 缩放因子
     */
    private double zoom;
    /**
     * 点位总数
     */
    private short pointCount;
    /**
     * 基数
     */
    private double baseValue;
    /**
     * 表达式
     */
    private String formula;


    protected Message(short unitId, String deviceGuid, byte fc, int offset, String pointCode) {
        this.unitId = unitId;
        this.deviceGuid = deviceGuid;
        this.offset = offset;
        this.pointCode = pointCode;
        this.fc = fc;
    }

    protected Message(int serverId, short unitId, byte fc, int offset, String deviceGuid, String pointCode,
                      DataType dataType, String pointDataType, boolean littleEndian, boolean registerSwap, double zoom,
                      short pointCount, double baseValue, String formula) {
        this.channelId = serverId;
        this.unitId = unitId;
        this.fc = fc;
        this.offset = offset;
        this.deviceGuid = deviceGuid;
        this.pointCode = pointCode;
        this.dataType = dataType;
        this.littleEndian = littleEndian;
        this.registerSwap = registerSwap;
        this.zoom = zoom;
        this.pointDataType = pointDataType;
        this.pointCount = pointCount;
        this.baseValue = baseValue;
        this.formula = formula;
    }

    /**
     * 针对线圈读取和寄存器中状态量的偏移
     *
     * @return
     */
    public abstract short dataOffset();

    public abstract byte bitCount();

    /**
     * 获取在响应数组下的index
     *
     * @return
     */
    public abstract int byteOffset(int startOffset, int skipByte);

    public Number toNumber(byte[] data, int startOffset, int skipByte) {
        final int realOffset = byteOffset(startOffset, skipByte);
        return Bits.toNumber(data, realOffset, dataType, littleEndian, registerSwap, zoom, baseValue, bitCount());
    }


    /**
     * 相同unitId排序有意义
     * <p>offset 相同时，校验dataoffset是否相同</p>
     *
     * @param o
     * @return
     */
    @Override
    public int compareTo(Message o) {
        int diff = offset - o.offset;
        if (diff == 0 && dataOffset() >= 0) {
            return dataOffset() - o.dataOffset();
        }
        return diff;
    }

}
